package ua.lviv.franko.solvers.stressanalysis.oneDimention;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

import ua.lviv.franko.integration.IFunction1D;
import ua.lviv.franko.integration.MatrixFunction1D;
import ua.lviv.franko.integration.VectorFunction1D;
import ua.lviv.franko.integration.VectorFunctionWithoutDerivatives1D;
import ua.lviv.franko.visualisation.NewOkCancelDialog;

public class BubbleElement extends AbstractElement1D {

	private static final int	NUM_OF_BUBBLE_FUNCTIOS	= 7;

	public BubbleElement(EquationParameters eParams, int elNumber, final double left, final double right) {
		super(eParams, elNumber, left, right);
		baseFunctions = new ArrayList<IFunction1D>();
		IFunction1D N1 = new IFunction1D() {
			@Override
			public double calculate(double x) {
				double xReal = (2 * x - left - right) / (right - left);
				if ((x < left) || (x > right)) {
					return 0.0;
				}
				return (1 - xReal) / 2.0;
			}

			@Override
			public double calculateDerivaty(double x) {
				if ((x < left) || (x > right)) {
					return 0.0;
				}
				return -1.0 / (right - left);
			}
		};
		baseFunctions.add(N1);

		IFunction1D N3 = new IFunction1D() {
			@Override
			public double calculate(double x) {
				double xReal = (2 * x - left - right) / (right - left);
				if ((x < left) || (x > right)) {
					return 0.0;
				}
				// return (5 * Math.pow(xReal, 3) - 4 * xReal) / (2.0 *
				// Math.sqrt( 10 ));
				return Math.sqrt(3.0 / 8.0) * (xReal * xReal - 1);
			}

			@Override
			public double calculateDerivaty(double x) {
				double xReal = (2 * x - left - right) / (right - left);
				if ((x < left) || (x > right)) {
					return 0.0;
				}
				// return (15 * Math.pow(xReal, 2) - 4 ) / (2.0 * Math.sqrt( 10
				// )) * (2.0 / (right-left));
				return Math.sqrt(3.0 / 2.0) * (xReal) * (2.0 / (right - left));
			}
		};
		baseFunctions.add(N3);

		IFunction1D N4 = new IFunction1D() {
			@Override
			public double calculate(double x) {
				double xReal = (2 * x - left - right) / (right - left);
				if ((x < left) || (x > right)) {
					return 0.0;
				}
				return Math.sqrt(5.0 / 8.0) * (Math.pow(xReal, 3) - xReal);
			}

			@Override
			public double calculateDerivaty(double x) {
				double xReal = (2 * x - left - right) / (right - left);
				if ((x < left) || (x > right)) {
					return 0.0;
				}
				return Math.sqrt(5.0 / 8.0) * (3 * Math.pow(xReal, 2) - 1.0) * (2.0 / (right - left));
			}
		};
		baseFunctions.add(N4);

		IFunction1D N5 = new IFunction1D() {
			@Override
			public double calculate(double x) {
				double xReal = (2 * x - left - right) / (right - left);
				if ((x < left) || (x > right)) {
					return 0.0;
				}
				return Math.sqrt(7.0 / 8.0) * (5.0 * Math.pow(xReal, 4) / 4.0 - xReal * xReal * 3.0 / 2.0 + 0.25);
			}

			@Override
			public double calculateDerivaty(double x) {
				double xReal = (2 * x - left - right) / (right - left);
				if ((x < left) || (x > right)) {
					return 0.0;
				}
				return Math.sqrt(7.0 / 8.0) * (5.0 * Math.pow(xReal, 3) - xReal * 3.0) * (2.0 / (right - left));
			}
		};
		baseFunctions.add(N5);

		IFunction1D N6 = new IFunction1D() {
			@Override
			public double calculate(double x) {
				double xReal = (2 * x - left - right) / (right - left);
				if ((x < left) || (x > right)) {
					return 0.0;
				}
				return Math.sqrt(9.0 / 2.0) / 8.0 * (7.0 * Math.pow(xReal, 5) - 10.0 * Math.pow(xReal, 3) + xReal * 3.0);
			}

			@Override
			public double calculateDerivaty(double x) {
				double xReal = (2 * x - left - right) / (right - left);
				if ((x < left) || (x > right)) {
					return 0.0;
				}
				return Math.sqrt(9.0 / 2.0) / 8.0 * (35.0 * Math.pow(xReal, 4) - 30.0 * Math.pow(xReal, 2) + 3.0) * (2.0 / (right - left));
			}
		};
		baseFunctions.add(N6);

		IFunction1D N7 = new IFunction1D() {
			@Override
			public double calculate(double x) {
				double xReal = (2 * x - left - right) / (right - left);
				if ((x < left) || (x > right)) {
					return 0.0;
				}
				return Math.sqrt(11.0 / 2.0) / 16.0 * (21.0 * Math.pow(xReal, 6) - 35.0 * Math.pow(xReal, 4) + xReal * xReal * 15.0 - 1);
			}

			@Override
			public double calculateDerivaty(double x) {
				double xReal = (2 * x - left - right) / (right - left);
				if ((x < left) || (x > right)) {
					return 0.0;
				}
				return Math.sqrt(11.0 / 2.0) / 16.0 * (126.0 * Math.pow(xReal, 5) - 140.0 * Math.pow(xReal, 3) + xReal * 30.0) * (2.0 / (right - left));
			}
		};
		baseFunctions.add(N7);

		IFunction1D N8 = new IFunction1D() {
			@Override
			public double calculate(double x) {
				double xReal = (2 * x - left - right) / (right - left);
				if ((x < left) || (x > right)) {
					return 0.0;
				}
				return Math.sqrt(13.0 / 2.0) / 16.0 * (231.0 / 7.0 * Math.pow(xReal, 7) - 63.0 * Math.pow(xReal, 5) + 35.0 * Math.pow(xReal, 3) - 5.0 * xReal);
			}

			@Override
			public double calculateDerivaty(double x) {
				double xReal = (2 * x - left - right) / (right - left);
				if ((x < left) || (x > right)) {
					return 0.0;
				}
				return Math.sqrt(13.0 / 2.0) / 16.0 * (231.0 * Math.pow(xReal, 6) - 315.0 * Math.pow(xReal, 4) + 105.0 * Math.pow(xReal, 2) - 5.0) * (2.0 / (right - left));
			}
		};
		baseFunctions.add(N8);

		IFunction1D N9 = new IFunction1D() {
			@Override
			public double calculate(double x) {
				double xReal = (2 * x - left - right) / (right - left);
				if ((x < left) || (x > right)) {
					return 0.0;
				}
				return Math.sqrt(13.0 / 2.0) / 16.0
						* (429.0 / 8.0 * Math.pow(xReal, 8) - 231.0 / 2.0 * Math.pow(xReal, 6) + 315.0 / 4.0 * Math.pow(xReal, 4) - 35.0 / 2.0 * xReal * xReal + 5.0 / 8.0);
			}

			@Override
			public double calculateDerivaty(double x) {
				double xReal = (2 * x - left - right) / (right - left);
				if ((x < left) || (x > right)) {
					return 0.0;
				}
				return Math.sqrt(13.0 / 2.0) / 16.0 * (429.0 * Math.pow(xReal, 7) - 231.0 * 3.0 * Math.pow(xReal, 5) + 315.0 * Math.pow(xReal, 3) - 35.0 * xReal)
						* (2.0 / (right - left));
			}
		};
		baseFunctions.add(N9);

		IFunction1D N2 = new IFunction1D() {
			@Override
			public double calculate(double x) {
				double xReal = (2 * x - left - right) / (right - left);
				if ((x < left) || (x > right)) {
					return 0.0;
				}
				return (1 + xReal) / 2.0;
			}

			@Override
			public double calculateDerivaty(double x) {
				if ((x < left) || (x > right)) {
					return 0.0;
				}
				return 1.0 / (right - left);
			}
		};
		baseFunctions.add(N2);

	}

	@Override
	public double[] calculate(double x, double[] results) {
		double[] res = new double[3];
		List<Integer> list = this.getNodesNumbers();
		for (int i = 0; i < list.size(); i++) {
			double val = baseFunctions.get(i).calculate(x);
			res[0] += val * results[3 * list.get(i)];
			res[1] += val * results[3 * list.get(i) + 1];
			res[2] += val * results[3 * list.get(i) + 2];
		}
		return res;
		// new double[]{
		// v1*results[3 * elNumber] + v2 * results[3 * (elNumber + 1)],
		// v1*results[3 * elNumber + 1] + v2 * results[3 * (elNumber + 1) + 1],
		// v1*results[3 * elNumber + 2] + v2 * results[3 * (elNumber + 1) + 2]
		// };
	}

	@Override
	protected ElementMatrix calculateSmallData(int i, int j) {
		List<Integer> numbers = new ArrayList<Integer>(2);
		numbers.add(elNumber * (NUM_OF_BUBBLE_FUNCTIOS + 1) + i);
		numbers.add(elNumber * (NUM_OF_BUBBLE_FUNCTIOS + 1) + j);
		ElementMatrix res = new ElementMatrix(this.getNodesNumbers());

		final IFunction1D fi = baseFunctions.get(i);
		final IFunction1D fj = baseFunctions.get(j);

		res.matrix = gau.integrate(left, right, new MatrixFunction1D() {

			@Override
			public double[][] calculate(double x) {
				double fiDer = fi.calculateDerivaty(x);
				double fjDer = fj.calculateDerivaty(x);
				double fiVal = fi.calculate(x);
				double fjVal = fj.calculate(x);
				double realXValue = (1 - x) / 2.0 * left + (1 + x) / 2.0 * right;
				double a1 = eParams.getA1().calculate(realXValue);
				double k1 = eParams.getK1().calculate(realXValue);
				return new double[][] {
						// first row
						{ kKk1 / a1 * fiDer * fjDer + a1 * k1 * k1 * kKk2 * fiVal * fjVal, kKk1 / a1 * k1 * fjVal * fiDer - k1 * kKk2 * fjDer * fiVal,
								-a1 * k1 * kKk2 * fiVal * fjVal },
						// second row
						{ -kKk2 * k1 * fjVal * fiDer + k1 * kKk1 * fjDer * fiVal, kKk2 / a1 * fjDer * fiDer + a1 * k1 * k1 * kKk1 * fiVal * fjVal, kKk2 * fjVal * fiDer },
						// third row
						{ -a1 * kKk2 * k1 * fiVal * fjVal, kKk2 * fjDer * fiVal, kKk3 / a1 * fjDer * fiDer + a1 * kKk2 * fiVal * fjVal } };
			}

			@Override
			public double[][] calculateDerivaty(double x) {

				return null;
			}

			@Override
			public int matrixSize() {
				return 3;
			}
		});

		// right hand side
		res.vector = gau.integrate(left, right, new VectorFunction1D() {

			@Override
			public double[] calculate(double x) {
				double fiVal = fi.calculate(x);
				double h = eParams.getH() / 2.0;
				double realXValue = (1 - x) / 2.0 * left + (1 + x) / 2.0 * right;
				double k1 = eParams.getK1().calculate(realXValue);

				double rho1Val = rho1.calculate(realXValue);
				double rho3Val = rho3.calculate(realXValue);
				double mu1Val = mu1.calculate(realXValue);

				double sigma11Minus = eParams.getSigma11Minus().calculate(realXValue);
				double sigma11Plus = eParams.getSigma11Plus().calculate(realXValue);

				double sigma13Minus = eParams.getSigma13Minus().calculate(realXValue);
				double sigma13Plus = eParams.getSigma13Plus().calculate(realXValue);

				double sigma33Minus = eParams.getSigma33Minus().calculate(realXValue);
				double sigma33Plus = eParams.getSigma33Plus().calculate(realXValue);

				return new double[] { fiVal * ((1 + k1 * h) * sigma13Plus + (1 - k1 * h) * sigma13Minus + rho1Val),
						fiVal * ((1 + k1 * h) * sigma33Plus + (1 - k1 * h) * sigma33Minus + rho3Val),
						fiVal * (((1 + k1 * h) * sigma11Plus - (1 - k1 * h) * sigma11Minus) * h + mu1Val),

				};
			}

			@Override
			public double[] calculateDerivaty(double x) {

				return null;
			}

			@Override
			public int vectorSize() {

				return 3;
			}
		});

		return res;
	}

	@Override
	public List<Integer> getNodesNumbers() {
		List<Integer> numbers = new ArrayList<Integer>(2);

		numbers.add(elNumber * (NUM_OF_BUBBLE_FUNCTIOS + 1));
		for (int i = 1; i <= NUM_OF_BUBBLE_FUNCTIOS; i++) {
			numbers.add(elNumber * (NUM_OF_BUBBLE_FUNCTIOS + 1) + i);
		}
		numbers.add((elNumber + 1) * (NUM_OF_BUBBLE_FUNCTIOS + 1));

		return numbers;
	}
	
	@Override
	protected double[] calculateSmallUpdatedVector(int i,final EquationParameters parameters) {
		final IFunction1D fi = baseFunctions.get(i);
		return gau.integrate(left, right, new VectorFunctionWithoutDerivatives1D(3) {
			
			@Override
			public double[] calculate(double x) {
				double fiVal = fi.calculate(x);
				double h = parameters.getH() / 2.0;
				double realXValue = (1 - x) / 2.0 * left + (1 + x) / 2.0 * right;
				double k1 = parameters.getK1().calculate(realXValue);

				double sigma11Minus = parameters.getSigma11Minus().calculate(realXValue);
				double sigma11Plus = parameters.getSigma11Plus().calculate(realXValue);

				double sigma13Minus = parameters.getSigma13Minus().calculate(realXValue);
				double sigma13Plus = parameters.getSigma13Plus().calculate(realXValue);

				double sigma33Minus = parameters.getSigma33Minus().calculate(realXValue);
				double sigma33Plus = parameters.getSigma33Plus().calculate(realXValue);

				return new double[] { fiVal * ((1 + k1 * h) * sigma13Plus + (1 - k1 * h) * sigma13Minus ),
						fiVal * ((1 + k1 * h) * sigma33Plus + (1 - k1 * h) * sigma33Minus ),
						fiVal * (((1 + k1 * h) * sigma11Plus - (1 - k1 * h) * sigma11Minus) * h ),

				};
			}
		});
	}

	public static void main(String[] args) {
		double a = 0;
		double b = 1;
		BubbleElement bbel = new BubbleElement(new EquationParameters(), 0, a, b);

		List<XYSeries> list = new ArrayList<XYSeries>();
		for (int i = 0; i < bbel.baseFunctions.size(); i++) {
			list.add(new XYSeries("result" + i, true, true));
		}

		int NNN = 30;
		double hhh = (b - a) / NNN;
		for (int k = 0; k <= NNN; k++) {
			double x = a + k * hhh;
			for (int i = 0; i < bbel.baseFunctions.size(); i++) {
				list.get(i).add(x, bbel.baseFunctions.get(i).calculate(x));
			}

		}

		XYSeriesCollection col = new XYSeriesCollection();
		for (Iterator<XYSeries> iterator = list.iterator(); iterator.hasNext();) {
			col.addSeries(iterator.next());
		}
		NewOkCancelDialog d = new NewOkCancelDialog(null, false, col);
		d.setLocationRelativeTo(null);
		d.setVisible(true);
	}

}
